
#include <Archaism/Socket.h>
#include <WinSock2.h>
#include <WS2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

_BeginNamespace(Radix)
	
Socket::Socket()
	:mHandle(INVALID_SOCKET)
{}

Socket::~Socket()
{}

bool Socket::startup()
{
	WSADATA wsaData;
	ZeroMemory(&wsaData, sizeof(wsaData));
	if(0 != ::WSAStartup(MAKEWORD(2, 2), &wsaData))
		return false;
	return true;
}

void Socket::cleanup()
{
	::WSACleanup();
}

bool Socket::connect(const char* hostName, const char* serviceName)
{
	addrinfo hints;
	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	
	addrinfo* ai = nullptr;
	if(0 != ::getaddrinfo(hostName, serviceName, &hints, &ai))
		return false;

	for(addrinfo* ptr = ai; ptr != nullptr; ptr = ai->ai_next)
	{
		mHandle = ::socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
		if(mHandle == INVALID_SOCKET)
			continue;
		if(SOCKET_ERROR == ::connect(mHandle, ptr->ai_addr, (int)ptr->ai_addrlen))
		{
			::closesocket(mHandle);
			continue;
		}
		break;
	}

	::freeaddrinfo(ai);
	if(mHandle == INVALID_SOCKET)
		return false;
	
	return true;
}

bool Socket::listen(const char* hostName, const char* serviceName)
{
	addrinfo hints;
	ZeroMemory(&hints, sizeof(hints));
	hints.ai_family = AF_INET;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	hints.ai_flags = AI_PASSIVE;
	
	addrinfo* ai = nullptr;
	if(0 != ::getaddrinfo(hostName, serviceName, &hints, &ai))
		return false;

	mHandle = ::socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
	if(mHandle == INVALID_SOCKET)
	{
		::freeaddrinfo(ai);
		return false;
	}

	if(SOCKET_ERROR == ::bind(mHandle, ai->ai_addr, (int)ai->ai_addrlen))
	{
		::freeaddrinfo(ai);
		::closesocket(mHandle);
		return false;
	}

	freeaddrinfo(ai);

	if(SOCKET_ERROR == ::listen(mHandle, SOMAXCONN))
	{
		::closesocket(mHandle);
		return false;
	}

	return true;
}

Socket Socket::accept()
{
	Socket clientSocket;
	clientSocket.mHandle = ::accept(mHandle, nullptr, nullptr);
	return clientSocket;
}

void Socket::close()
{
	::closesocket(mHandle);
}

int Socket::recv(uchar* buffer, uint length)
{
	return ::recv(mHandle, (char*)buffer, length, 0);
}

int Socket::send(uchar* buffer, uint length)
{
	return ::send(mHandle, (char*)buffer, length, 0);
}

bool Socket::available() const
{
	return mHandle != INVALID_SOCKET;
}

_EndNamespace(Radix)
